package cn.bossfriday.common.plugin;

import cn.bossfriday.common.exception.ServiceRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;

/**
 * PluginResourcePostProcessor
 * <p>
 * Spring 容器在实例化一个 Bean 时，会在初始化前后自动调用 BeanPostProcessor 来支持对Bean的额外的处理或修改；
 * 这里的目的是实现 @PluginResource 的自动注入；
 *
 * @author chenx
 */
@Slf4j
public class PluginResourcePostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> clazz = bean.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            PluginResource annotation = field.getAnnotation(PluginResource.class);
            if (annotation != null) {
                this.injectBean(bean, field, annotation);
                log.info(" - Bean {} ", clazz.getName());
            }
        }

        return bean;
    }

    /**
     * injectBean
     */
    private void injectBean(Object bean, Field field, PluginResource annotation) {
        try {
            Class<?> type = annotation.type();
            if (type == Object.class) {
                type = field.getType();
            }

            Object dependency = PluginSpringContext.getBean(type);
            if (dependency != null) {
                ReflectionUtils.makeAccessible(field);
                ReflectionUtils.setField(field, bean, dependency);
            } else {
                throw new ServiceRuntimeException("No bean found for type: " + type.getName());
            }
        } catch (Exception ex) {
            throw new ServiceRuntimeException("Failed to inject PluginResource into " + bean.getClass().getName());
        }
    }
}

